package com.hyacinth.lotterysystem.service.mq;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.hyacinth.lotterysystem.common.exception.ServiceException;
import com.hyacinth.lotterysystem.common.utils.EmailUtil;
import com.hyacinth.lotterysystem.common.utils.JacksonUtil;
import com.hyacinth.lotterysystem.dao.entity.ActivityPrizeDO;
import com.hyacinth.lotterysystem.dao.entity.WinningRecordDO;
import com.hyacinth.lotterysystem.dao.mapper.ActivityPrizeMapper;
import com.hyacinth.lotterysystem.dao.mapper.WinningRecordMapper;
import com.hyacinth.lotterysystem.dto.param.Winner;
import com.hyacinth.lotterysystem.dto.req.DrawPrizeReq;
import com.hyacinth.lotterysystem.service.ActivityService;
import com.hyacinth.lotterysystem.service.DrawPrizeService;
import com.hyacinth.lotterysystem.service.activityStatus.ActivityStatusManager;
import com.hyacinth.lotterysystem.service.activityStatus.ConvertActivityStatusDTO;
import com.hyacinth.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import com.hyacinth.lotterysystem.service.enums.ActivityStatusEnum;
import com.hyacinth.lotterysystem.service.enums.ActivityUserStatusEnum;
import jakarta.annotation.Resource;
import org.apache.commons.mail.EmailException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;

import static com.hyacinth.lotterysystem.common.config.DirectRabbitConfig.QUEUE_NAME;

@Component
@RabbitListener(queues = QUEUE_NAME)
public class MqReceiver {
    private static final Logger logger = LoggerFactory.getLogger(MqReceiver.class);

    @Resource
    private DrawPrizeService drawPrizeService;

    @Resource
    private ActivityStatusManager activityStatusManager;

    @Resource
    private ThreadPoolTaskExecutor asyncServiceExecutor;

    @Resource
    private EmailUtil emailUtil;

    @Resource
    private ActivityPrizeMapper activityPrizeMapper;

    @Resource
    private ActivityService activityService;

    @Resource
    private WinningRecordMapper winningRecordMapper;

    @RabbitHandler
    public void process(Map<String, String> message) throws Exception {
        // 成功接收消息
        logger.info("MQ成功接收到消息 -> message: {}", JacksonUtil.writeValueAsString(message));
        String data = message.get("messageData");
        DrawPrizeReq req = JacksonUtil.readValue(data, DrawPrizeReq.class);
        try {
            // 校验抽奖请求
            if (!drawPrizeService.checkDrawPrizeParam(req))
                return;
            // 状态扭转
            statusConvert(req);
            // 保存中奖者名单
            List<WinningRecordDO> winningRecordDOList = drawPrizeService.saveWinnerRecords(req);
            // 通知中奖者
            asyncServiceExecutor.execute(() -> {
                try {
                    emailUtil.senWinningRecord(winningRecordDOList);
                } catch (EmailException e) {
                    e.printStackTrace();
                }
            });

        } catch (ServiceException e) {
            // 如果异常 进行回滚
            logger.error("处理MQ异常 {}, {}", e.getCode(), e.getMsg(), e);
            rollback(req);
            throw e;
        } catch (Exception e) {
            // 如果异常 进行回滚
            logger.error("处理MQ异常 ", e);
            rollback(req);
            throw e;
        }
    }

    public void rollback(DrawPrizeReq req) {
        // 可能是扭转状态 or 保存中奖者名单 之后出现的异常  回滚状态: 活动 奖品 人员
        // 先判断状态是否需要回滚 （人员或者奖品 不能判断活动）
        if (!statusNeedRollback(req))
            return;

        // 回滚状态
        rollbackStatus(req);

        // 判断中奖名单是否需要回滚
        if (!winnerNeedRollback(req))
            return;
        // 回滚中奖者名单
        rollbackWinner(req);
    }

    /**
     *
     * 回滚中奖记录
     *
     * @param req
     */
    private void rollbackWinner(DrawPrizeReq req) {
        drawPrizeService.deleteRecords(req.getActivityId(), req.getPrizeId());
    }

    private boolean winnerNeedRollback(DrawPrizeReq req) {
        // 判断活动中的奖品有没有中奖者
        LambdaQueryWrapper<WinningRecordDO> winningRecordDOLambdaQueryWrapper = Wrappers.lambdaQuery(WinningRecordDO.class)
                .eq(WinningRecordDO::getActivityId, req.getActivityId())
                .eq(WinningRecordDO::getPrizeId, req.getPrizeId());

        return winningRecordMapper.selectCount(winningRecordDOLambdaQueryWrapper) > 0;
    }

    private void rollbackStatus(DrawPrizeReq req) {
        // 使用 ActivityStatusManager
        ConvertActivityStatusDTO activityStatusDTO = new ConvertActivityStatusDTO();
        activityStatusDTO.setActivityId(req.getActivityId());
        activityStatusDTO.setPrizeId(req.getPrizeId());
        activityStatusDTO.setUserIds(req.getWinnerList().stream()
                .map(Winner::getUserId)
                .toList());
        activityStatusDTO.setActivityStatusEnum(ActivityStatusEnum.RUNNING);
        activityStatusDTO.setPrizeStatusEnum(ActivityPrizeStatusEnum.INIT);
        activityStatusDTO.setUserStatusEnum(ActivityUserStatusEnum.INIT);
        activityStatusManager.rollbackHandlerEvent(activityStatusDTO);

        // 回滚之后 更新缓存
        activityService.cacheActivity(req.getActivityId());
    }

    private boolean statusNeedRollback(DrawPrizeReq req) {
        LambdaQueryWrapper<ActivityPrizeDO> activityPrizeDOLambdaQueryWrapper = Wrappers.lambdaQuery(ActivityPrizeDO.class)
                .eq(ActivityPrizeDO::getActivityId, req.getActivityId())
                .eq(ActivityPrizeDO::getPrizeId, req.getPrizeId());

        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectOne(activityPrizeDOLambdaQueryWrapper);
        return activityPrizeDO.getStatus().equalsIgnoreCase(ActivityPrizeStatusEnum.COMPLETED.name());
    }

    private void statusConvert(DrawPrizeReq req) {
        ConvertActivityStatusDTO convertActivityStatusDTO = new ConvertActivityStatusDTO();
        convertActivityStatusDTO.setActivityId(req.getActivityId());
        convertActivityStatusDTO.setPrizeId(req.getPrizeId());
        convertActivityStatusDTO.setActivityStatusEnum(ActivityStatusEnum.COMPLETED);
        convertActivityStatusDTO.setPrizeStatusEnum(ActivityPrizeStatusEnum.COMPLETED);
        convertActivityStatusDTO.setUserStatusEnum(ActivityUserStatusEnum.COMPLETED);
        convertActivityStatusDTO.setUserIds(req.getWinnerList().stream()
                .map(Winner::getUserId)
                .toList());

        activityStatusManager.handlerEvent(convertActivityStatusDTO);
    }

}
